package com.google.code.sfa.nextgen;

import java.util.Random;
import java.util.List;
import java.util.ArrayList;
import org.biojava.bio.symbol.*;
import org.biojava.bio.seq.DNATools;
import com.google.code.sfa.util.*;
import com.google.code.sfa.parse.*;
import com.google.code.sfa.tigr.*;

public class Simulator {

    private SymbolList reference;
    private SymbolList subject;
    private List<Trace> traces;
    private Random rand;

    public Simulator( SymbolList reference, float simularity, float coverage, float readAccuracy,
		      int minLength, int maxLength,int seed) {
	rand = new Random(seed);
	traces = new ArrayList<Trace>();
	this.reference = reference;
	try {
	    this.subject = DNATools.createDNA(reference.seqString());
	} catch( IllegalSymbolException e) {
	    e.printStackTrace();
	}
	introduceRandomChanges(  (int) ((1-simularity) * reference.length()));
	breakIntoFragments( (int) (reference.length() * coverage), readAccuracy, minLength, maxLength);	
    }

    public SymbolList getSubject() {
	return subject;
    }
    
    private void introduceRandomChanges(int changes) {
        for( int i = 0; i < changes; i++) {
	    int pos = rand.nextInt(subject.length())+1;
	    introduceRandomChange(subject, pos);
	}
    }

    private void introduceRandomChange(SymbolList list, int pos) {
	int change = rand.nextInt(10);
	Edit e;
	if( change < 6) {
	    // mutation
	    e = new Edit( pos, 1, randomDNA());
	} else if( change < 8) {
	    // insertion
	    e = new Edit( pos, 0, randomDNA());
	} else {
	    // deletion
	    e = new Edit( pos, 1, SymbolList.EMPTY_LIST);
	}
	try {
	    list.edit( e);
	} catch(IllegalAlphabetException ex) {
	    ex.printStackTrace();
	    return;
	}

    }

    private SymbolList randomDNA() {
	int base = rand.nextInt(4);
	
	try {
	    if( base == 0) {
		return DNATools.createDNA("a");
	    } else if( base == 1) {
		return DNATools.createDNA("t");
	    } else if( base == 2) {
		return DNATools.createDNA("c");
	    } else {
		return DNATools.createDNA("g");
	    }
	} catch( IllegalSymbolException e) {
	    e.printStackTrace();
	}
	return SymbolList.EMPTY_LIST;
    }

    private void breakIntoFragments(int bases, float accuracy, int minLength, int maxLength) {

	int basesBuilt = 0;
	int length;
	while( basesBuilt < bases) {
	    length = rand.nextInt(maxLength-minLength+1) + minLength;
	    basesBuilt += length;
	    
	    traces.add( createFragment( length, accuracy));
	}

    }

    private Trace createFragment( int length, float accuracy) {

	int includedPos = rand.nextInt(subject.length()) + 1;
	int pos = rand.nextInt( length) + includedPos - length + 1;
	if( pos < 1) {
	    pos = 1;
	}
	if( pos + length > subject.length()) {
	    pos = subject.length() - length + 1;
	}
	SymbolList seq = null;
	try {
	    seq = DNATools.createDNA( subject.subStr(pos, pos + length - 1));
	} catch( IllegalSymbolException e) {
	    e.printStackTrace();
	}
	int errors = 0;
	for( int i = seq.length()-1; i > 1; i--) {
	    if( rand.nextDouble() > accuracy) {
		introduceRandomChange( seq, i);
		errors++;
	    }
	}

	String id = "P" + pos + "E" + errors;
	Trace t = new Trace( id, id, seq, true);
	return t;
    }

    public List<Trace> getTraces() {
	return traces;
    }

}
   